package com.threefivework.mymall.service.impl;

import com.google.common.collect.Maps;
import com.threefivework.mymall.dao.mapper.BuyerMapper;
import com.threefivework.mymall.dao.mapper.MemIntegralMapper;
import com.threefivework.mymall.dao.mapper.PaymentIntegralMapper;
import com.threefivework.mymall.dao.mapper.PaymentRecordMapper;
import com.threefivework.mymall.model.domain.*;
import com.threefivework.mymall.model.util.UnifiedOrderBeanUtil;
import com.threefivework.mymall.service.PaymentService;
import com.threefivework.mymall.service.WechatClient;
import com.threefivework.mymall.util.MySeq;
import com.threefivework.mymall.util.XmlUtil;
import com.threefivework.mymall.wechat.constant.APIConstants;
import com.threefivework.mymall.wechat.constant.WXConfigConstants;
import com.threefivework.mymall.wechat.constant.WXPayConstants;
import com.threefivework.mymall.wechat.response.PaymentNotifyResponse;
import com.threefivework.mymall.wechat.request.UnifiedOrderRequest;
import com.threefivework.mymall.wechat.response.UnifiedOrderResponse;
import com.threefivework.mymall.wechat.util.WXPayUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.util.*;

/**
 * @author yuanshi.fu
 * @description:
 * @date 2018/6/27 上午10:01
 */
@Service
public class PaymentServiceImpl implements PaymentService {

    private static final Logger logger = LoggerFactory.getLogger(PaymentServiceImpl.class);

    @Autowired
    private WechatClient client;

    @Autowired
    private PaymentRecordMapper paymentRecordMapper;

    @Autowired
    private PaymentIntegralMapper paymentIntegralMapper;

    @Autowired
    private MemIntegralMapper memIntegralMapper;

    @Autowired
    private BuyerMapper buyerMapper;

    @Override
    public String unifiedOrder(TradeDTO tradeDTO) throws Exception {

        //todo 验证相关参数
        Assert.notNull(tradeDTO, "tradeDTO不能为空");

        //封装生成订单
        UnifiedOrderRequest request = new UnifiedOrderRequest();
        request.setAppId(WXConfigConstants.APP_ID);
        request.setMchId(WXConfigConstants.MCH_ID);
        request.setDeviceInfo(WXConfigConstants.DEVICE_INFO);
        String pay = MySeq.getPay();
        request.setItemDesc(pay + "费用");
        request.setNonceStr(UUID.randomUUID().toString().replaceAll("-", ""));
        request.setNotifyUrl(APIConstants.WECHAT_PAY_NOTIFY_URL);
        Map<String, Object> accessTokenMap = client.getAccessToken(tradeDTO.getCode());
        String openId = "";
        if(!accessTokenMap.isEmpty()) {
            openId = (String)accessTokenMap.get("openid");
        }
        Assert.notNull(openId, "公众号支付openid不能为空");
        request.setOpenid(openId);
        request.setOutTradeNo(pay);
        //生成签名
        Map<String, String> reqSignData = UnifiedOrderBeanUtil.unifiedOrderRequest2SignMap(request);
        String sign = WXPayUtil.generateSignature(reqSignData, WXConfigConstants.KEY, WXPayConstants.SignType.MD5);
        request.setSign(sign);
        request.setTotalFee(tradeDTO.getTotalFee().intValue());
        request.setSpbillCreateIp(tradeDTO.getSpbillCreateIp());
        request.setTradeType(WXConfigConstants.TradeTypeEnum.JSAPI.name());

        ResponseEntity responseEntity = client.unifiedOrder(request);
        if (null == responseEntity) {
            return "请求异常";
        }

        if (responseEntity.getStatusCodeValue() == HttpStatus.SC_OK) {
            UnifiedOrderResponse response = XmlUtil.xml2Bean(responseEntity.getBody().toString(), UnifiedOrderResponse.class);
            //todo 判断返回码及返回结果
            Optional<Buyer> buyerOpt = buyerMapper.getByOpenId(openId);
            if (buyerOpt.isPresent()) {
                logger.info("", "该用户已存在");
            } else {
                //保存该用户
                Buyer buyer = new Buyer();
                buyer.setOpenId(openId);
                buyerMapper.insert(buyer);
            }

            //预支付id返回给前端
            String prepayId = response.getPrepayId();
            return prepayId;
        }

        return "";
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 36000, rollbackFor = Exception.class)
    public String notify(Map<String, String> xmlMap, String callbackXml) {
        try {
            if (xmlMap.isEmpty()) {
                // 签名错误，如果数据里没有sign字段，也认为是签名错误
                logger.warn("================支付回调失败,xmlMap为空================");
                return WXPayConstants.FAIL;
            }
            //生成签名
            PaymentNotifyResponse notifyResponse = XmlUtil.xml2Bean(callbackXml, PaymentNotifyResponse.class);
            Map<String, String> callbackSignMap = XmlUtil.xmlBean2Map(notifyResponse);
            String callbackSign = WXPayUtil.generateSignature(callbackSignMap, WXConfigConstants.KEY, WXPayConstants.SignType.MD5);
            String sign = xmlMap.get(WXPayConstants.FIELD_SIGN);
            if (StringUtils.isNotEmpty(callbackSign) && StringUtils.equals(sign, callbackSign)) {
                //todo 事物处理
                //todo 账单记录及prepay_id
                PaymentRecord paymentRecord = new PaymentRecord();
                paymentRecord.setBuyerId(10L);
                paymentRecord.setPaymentAmount(new BigDecimal(notifyResponse.getTotalFee()));
                paymentRecord.setTradeId(Long.valueOf(notifyResponse.getOutTradeNo()));
                paymentRecord.setBuyerId(Long.valueOf(notifyResponse.getOpenId()));
                //todo 其他字段

                //保存消费记录
                paymentRecordMapper.insert(paymentRecord);

                //todo 获取积分的账目信息计算出当前消费所得积分
                BigDecimal total = paymentRecord.getPaymentAmount();
                List<PaymentIntegral> paymentIntegralList = paymentIntegralMapper.list();
                Optional<PaymentIntegral> paymentIntegralMatchOptional = paymentIntegralList.stream().filter(paymentIntegral -> (new BigDecimal(paymentIntegral.getIntegral()).compareTo(total)) >= 0)
                        .sorted(Comparator.comparing(PaymentIntegral::getPaymentAmountUpper)).findFirst();
                //todo 保存消费记录并更新积分
                return paymentIntegralMatchOptional.map(paymentIntegral -> {
                    Integer integral = paymentIntegral.getIntegral();
                    //保存积分数据
                    Integer openId = 1;
                    MemIntegral memIntegral = memIntegralMapper.getByPK(openId);
                    if (null == memIntegral) {
                        MemIntegral newMemIntegral = new MemIntegral();
                        newMemIntegral.setBuyerId(Long.valueOf(openId));
                        newMemIntegral.setNum(integral);
                        memIntegralMapper.insert(newMemIntegral);
                    } else {
                        Integer remainNum = memIntegral.getNum();
                        memIntegral.setNum(remainNum + integral);
                        memIntegralMapper.update(memIntegral);
                    }
                    return WXPayConstants.SUCCESS;
                }).orElse(WXPayConstants.FAIL);
            }
        } catch (Exception e) {
            logger.error("================支付回调异常================", e);
        }

        return WXPayConstants.FAIL;
    }
}
